using System;
using TTEngine.Graphics;
using Silk.NET.OpenGLES;
namespace TTEngine.Resource
{
    public static class DefShader
    {
        private const string BaseVS = @"#version 300 es
layout(location = 0) in vec3 position;//
layout(location = 1) in vec2 uv; 
layout(location = 2) in vec4 color; 
layout(location = 3) in vec4 ext; 
layout(location = 4) in vec4 uvLimit; 
uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProj;

out vec4 vColor;//
out vec2 vUv;//
flat out vec4 vUvLimit;//
flat out int vExt;
flat out int vLayer;
void main(void) 
{
    mat4 matrix = matProj*matView*matModel;
    gl_Position = matrix * vec4(position,1);// uViewProjMatrix * uModelMatrix * position;
    vColor = color;
    vUv = uv;
    vUvLimit = uvLimit;;
    vExt = int(ext.w);
    vLayer =int(ext.x);
}";

        private const string VSInstTBO = @"#version 300 es
//form vbo1 as mesh
layout(location = 0) in vec2 elemuv; 
//from vbo2 as inst
layout(location = 1) in vec4 posrotate;
layout(location = 2) in vec2 scale;
layout(location = 3) in vec4 color; //rgba32
layout(location = 4) in vec2 ext;

// layout(std140, column_major) uniform;
struct Sprite //remove chinese
{
    vec2 posLT;
    vec2 posRB;
    vec2 uvCenter;
    vec2 uvHalfSize;
    float uvlayer;
    float empty1;////remove chinese
    float empty2;
    float eff;
};
uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProj;
uniform sampler2D texelem;
out vec4 vColor;
out vec2 vUv;
flat out vec4 vUvLimit;//
flat out int vExt;
flat out int vLayer;
void main(void)
{ 
    //get sprite
    int ext = int(ext.x);
    int iuvx = ext%128 *4;
    int iuvy = ext/128;

    vec4 v0 = texelFetch(texelem,ivec2(iuvx+0,iuvy),0);
    vec4 v1 = texelFetch(texelem,ivec2(iuvx+1,iuvy),0);
    vec4 v2 = texelFetch(texelem,ivec2(iuvx+2,iuvy),0);
    
    Sprite s;
    s.posLT = v0.xy;
    s.posRB = v0.zw;
    s.uvCenter = v1.xy;
    s.uvHalfSize = v1.zw;
    s.uvlayer = v2.x;
    s.eff = v2.w;

    //----begin calc final pos
    vec2 poslocal;
    poslocal.x=elemuv.x<0.0?s.posLT.x:s.posRB.x;
    poslocal.y=elemuv.y<0.0?s.posLT.y:s.posRB.y;
    poslocal*=scale;

    float poslocallen = sqrt(dot(poslocal,poslocal));
    if(poslocallen<0.001)   
    {
        //too near to zeropoint no rotate
    }
    else
    {
        //--begin rotate
        float cosv = cos(posrotate.w);
        float sinv = sin(posrotate.w);
        
        vec2 poslocalnor = poslocal / poslocallen;
        poslocal.x = cosv*poslocalnor.x + -sinv*poslocalnor.y;
        poslocal.y = sinv*poslocalnor.x + cosv*poslocalnor.y;
        poslocal *= poslocallen;
        //--end rorate
    }
    vec4 pos = vec4(posrotate.xyz,1);

    //apply tran
    pos.xy+=poslocal;
    //----end finalpos
    mat4 matrix = matProj*matView*matModel;
    gl_Position = matrix * pos;
   
    //pass uv
    vec2 uv =  s.uvCenter + elemuv*s.uvHalfSize;
    vUvLimit.xy = s.uvCenter - s.uvHalfSize;
    vUvLimit.zw = s.uvCenter + s.uvHalfSize;
    vUv = uv;
    //pass color
    vColor = color;
    //pass ext
    vExt=int(s.eff);
    vLayer=int(s.uvlayer);
}";

        private const string VSInstUBO = @"#version 300 es
//form vbo1 as mesh
layout(location = 0) in vec2 elemuv; 
//from vbo2 as inst
layout(location = 1) in vec4 posrotate;
layout(location = 2) in vec2 scale;
layout(location = 3) in vec4 color; //rgba32
layout(location = 4) in vec2 ext;

layout(std140, column_major) uniform;
struct Sprite //
{
    vec2 posLT;
    vec2 posRB;
    vec2 uvCenter;
    vec2 uvHalfSize;
    vec4 ext;
};

uniform SpritesBlock {//
    //
    Sprite data[1023];    
    int endtag; //
} sprites;

uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProj;

out vec4 vColor;
out vec2 vUv;
flat out int vExt;
flat out int vLayer;
void main(void)
{ 
    //get sprite
    Sprite s = sprites.data[int(ext.x)];
   
    //----begin calc final pos
    vec2 poslocal;
    poslocal.x=elemuv.x<0.0?s.posLT.x:s.posRB.x;
    poslocal.y=elemuv.y<0.0?s.posLT.y:s.posRB.y;
    poslocal*=scale;

    //--begin rotate
    float poslocallen = sqrt(dot(poslocal,poslocal));
    if(poslocallen<0.001)   
    {
        //too near to zeropoint no rotate
    }
    else
    {
        float cosv = cos(posrotate.w);
        float sinv = sin(posrotate.w);
        
        vec2 poslocalnor = poslocal / poslocallen;
        poslocal.x = cosv*poslocalnor.x + -sinv*poslocalnor.y;
        poslocal.y = sinv*poslocalnor.x + cosv*poslocalnor.y;
        poslocal *= poslocallen;
    }
    //--end rorate

    vec4 pos = vec4(posrotate.xyz,1);

    //apply tran
    pos.xy+=poslocal;
    //----end finalpos
    mat4 matrix = matProj*matView*matModel;
    gl_Position = matrix * pos;
    
    //pass uv
    vec2 uv =  s.uvCenter + elemuv*s.uvHalfSize;
   
    vUv = uv;
    //pass color
    vColor = color;
    //pass ext
    vExt=int(s.ext.w);
    vLayer=int(s.ext.x);
}";

        private const string BaseFS = @"#version 300 es
precision highp float;//
precision highp sampler2DArray;
in vec2 vUv;//
in vec4 vColor;//
flat in int vExt;
flat in int vLayer;
    
layout(location = 0) out vec4 fragColor;

uniform sampler2DArray texRGBA;  //
uniform sampler2DArray texGray;
void main(void) 
{
    vec4 texc = vExt==0? texture(texRGBA,vec3(vUv,vLayer))
    : texture(texGray,vec3(vUv,vLayer));
    vec4 outc = vColor;
    int effect = vExt;//int(vExt.w);
    if(effect==0)//rgba model 
    {
    
        outc = vColor*texc;
        if(outc.a<0.1)discard;
    }
    else if(effect==1)//gray model
    {
        vec4 c = vec4(texc.r,texc.r,texc.r,1);
        outc = vColor * c;
    }
    else if(effect==4)//gray as alpha model
    {
        outc = vColor;
        outc.a *=texc.r; 
    }
   

    fragColor =  outc;
    gl_FragDepth = gl_FragCoord.z;
}";

        private const string BaseFSCut = @"#version 300 es
precision highp float;//
precision highp sampler2DArray;
in vec2 vUv;//
in vec4 vColor;//
flat in vec4 vUvLimit;
flat in int vExt;
flat in int vLayer;
    
layout(location = 0) out vec4 fragColor;

uniform sampler2DArray texRGBA;  //
uniform sampler2DArray texGray;
void main(void) 
{
    //bad test
    if(vUv.x<vUvLimit.x||vUv.x>vUvLimit.z||vUv.y<vUvLimit.y||vUv.y>vUvLimit.w)
    {
        discard;
    }
    vec4 texc = vExt==0? texture(texRGBA,vec3(vUv,vLayer))
    : texture(texGray,vec3(vUv,vLayer));
    vec4 outc = vColor;
    int effect = vExt;//int(vExt.w);
    if(effect==0)//rgba model 
    {
        outc = vColor*texc;
    }
    else if(effect==1)//gray model
    {
        vec4 c = vec4(texc.r,texc.r,texc.r,1);
        outc = vColor * c;
    }
    else if(effect==4)//gray as alpha model
    {
        outc = vColor;
        outc.a *=texc.r; 
    }
    fragColor =  outc;
}";

        private const string VSSimple = @"#version 300 es
layout(location = 0) in vec3 position;//
layout(location = 1) in vec2 uv; 
layout(location = 2) in vec4 color; 

uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProj;

out vec4 vColor;//
out vec2 vUv;//
//uniform sampler2D tex2;
void main(void) 
{
    //vec4 t1 = texelFetch(tex2,ivec2(0, 0), 0);
    mat4 matrix = matProj*matView*matModel;
    gl_Position = matrix * vec4(position,1);// uViewProjMatrix * uModelMatrix * position;
    vColor = color;
    vUv = uv;
}";

        private const string FSColor = @"#version 300 es
precision highp float;//
precision highp sampler2DArray;

in vec2 vUv;//
in vec4 vColor;//
        
layout(location = 0) out vec4 fragColor;
void main(void) 
{
    vec4 outc = vColor;
    fragColor = outc;
}";

        private const string FSSimple = @"#version 300 es
precision highp float;//
precision highp sampler2DArray;

in vec2 vUv;//
in vec4 vColor;//
        
layout(location = 0) out vec4 fragColor;

uniform sampler2D tex;  //
void main(void) 
{
    vec4 texc = texture(tex,vUv);
    vec4 outc = vColor;
    outc = vColor * texc;
    fragColor =  outc;
}";
        private const string FSGrayAsAlpha = @"#version 300 es
precision highp float;//
precision highp sampler2DArray;

in vec2 vUv;//
in vec4 vColor;//
        
layout(location = 0) out vec4 fragColor;

uniform sampler2D tex;  //
void main(void) 
{
    float a = texture(tex,vUv).r;
    vec4 texc =vec4(1,1,1,a);
    vec4 outc = vColor;
    outc = vColor * texc;
    fragColor =  outc;
}";
        private const string VSFeedback = @"#version 300 es
layout(location = 0) in vec3 position;//
layout(location = 1) in vec3 normal;//
out vec3 outPos;
out vec3 outNormal;
void main(void) 
{
    outPos = position + normal;
    outNormal = normal;
}";

        private const string FSEmpty = @"#version 300 es
precision mediump float;//
   
layout(location = 0) out vec4 fragColor;

void main(void) 
{
    fragColor =  vec4(0,0,0,1);
}";

        private const string FSTiledMap = @"#version 300 es
precision mediump float;//
precision highp sampler2DArray;

in vec2 vUv;//
 
uniform sampler2D texIndex; 
uniform sampler2D texTile; 
uniform float   tilesize;
uniform vec4     mapinfo;//xy map size, zw=tile texturesize
layout(location = 0) out vec4 fragColor;

void main(void) 
{
    //
    vec4 texc = texture(texIndex,vUv);
    float su =float(int(texc.r*255.0+0.9)); //tileindex x
    float sv =float(int(texc.g*255.0+0.9)); //tileindex y

    float su2 =float(int(texc.b*255.0+0.9)); //tileindex z
    float sv2 =float(int(texc.a*255.0+0.9)); //tileindex w

    //
    float subu = mod(vUv.x * mapinfo.x, 1.0);
    float subv = mod(vUv.y * mapinfo.y, 1.0);
    vec2 tilescale =vec2(tilesize,tilesize)/mapinfo.zw;
    vec2 tuv = vec2((su+subu)*tilescale.x,(sv+subv)*tilescale.y);
    vec2 tuv2 = vec2((su2+subu)*tilescale.x,(sv2+subv)*tilescale.y);

    //
    vec4 ttc = texture(texTile,tuv);
    vec4 ttc2 = texture(texTile,tuv2);

    vec4 outcolor = ttc2.a*ttc2 + (1.0-ttc2.a)*ttc;
    
    fragColor = outcolor;
}";

        /// <summary>
        /// 初始化内置着色器程序
        /// 这个函数会编译并链接所有内置的着色器程序，包括：
        /// 1. 默认着色器程序 (default)
        /// 2. 带裁剪的默认着色器程序 (default_cut)
        /// 3. 简单着色器程序 (simple)
        /// 4. 纯色着色器程序 (color)
        /// 5. 反馈着色器程序 (feedback)
        /// 6. 瓦片地图着色器程序 (tiledmap)
        /// 7. 实例化 UBO 着色器程序 (inst_ubo)
        /// 8. 实例化 TBO 着色器程序 (inst_tbo)
        /// </summary>
        /// <param name="gl">OpenGL ES 接口实例</param>
        public static void InitInnerShader(ResourcePackage package)
        {
         
            // 编译实例化 TBO 顶点着色器
            var vsInstTBO = new ShaderObj(package, ShaderType.VertexShader, VSInstTBO);
            // 编译实例化 UBO 顶点着色器
            var vsInstUBO = new ShaderObj(package, ShaderType.VertexShader, VSInstUBO);
            // 编译默认顶点着色器
            var vsDef = new ShaderObj(package, ShaderType.VertexShader, BaseVS);
            // 编译默认片段着色器
            var fsDef = new ShaderObj(package, ShaderType.FragmentShader, BaseFS);
            // 编译带裁剪的默认片段着色器
            var fsDefCut = new ShaderObj(package, ShaderType.FragmentShader, BaseFSCut);

            // 链接默认着色器程序
            if (vsDef != null && fsDef != null)
            {
                var shader = new ShaderProgram(package, vsDef, fsDef);
                shader.SetUniqueName("default");
            }
            // 链接带裁剪的默认着色器程序
            if (vsDef != null && fsDefCut != null)
            {
                var shader = new ShaderProgram(package, vsDef, fsDefCut);
                shader.SetUniqueName("default_cut");
            }
            // 编译简单顶点着色器
            var vsSim = new ShaderObj(package, ShaderType.VertexShader, VSSimple);
            vsSim.SetUniqueName("vs_simple");

            // 编译简单片段着色器
            var fsSim = new ShaderObj(package, ShaderType.FragmentShader, FSSimple);
            var fsSimGrayAsAlpha = new ShaderObj(package, ShaderType.FragmentShader, FSGrayAsAlpha);
            // 链接简单着色器程序
            if (vsSim != null && fsSim != null)
            {
                var shader = new ShaderProgram(package, vsSim, fsSim);
                shader.SetUniqueName("simple");
                var shaderGray = new ShaderProgram(package, vsSim, fsSimGrayAsAlpha);
                shaderGray.SetUniqueName("simpleGrayAsAlpha");
            }
            // 编译纯色片段着色器
            var fsColor = new ShaderObj(package, ShaderType.FragmentShader, FSColor);
            // 链接纯色着色器程序
            if (vsSim != null && fsColor != null)
            {
                var shader = new ShaderProgram(package, vsSim, fsColor);
                shader.SetUniqueName("color");
            }
            // 编译反馈顶点着色器
            var vsFeedback = new ShaderObj(package, ShaderType.VertexShader, VSFeedback);
            // 编译空片段着色器
            var fsEmpty = new ShaderObj(package, ShaderType.FragmentShader, FSEmpty);
            // 链接反馈着色器程序，指定输出变量
            if (vsFeedback != null && fsEmpty != null)
            {
                var shader = new ShaderProgram(package, vsFeedback, fsEmpty);
                shader.SetUniqueName("feedback");
            }
            // 编译瓦片地图片段着色器
            var fsTiledMap = new ShaderObj(package, ShaderType.FragmentShader, FSTiledMap);
            // 链接瓦片地图着色器程序
            if (vsDef != null && fsTiledMap != null)
            {
                var shader = new ShaderProgram(package, vsSim, fsTiledMap);
                shader.SetUniqueName("tiledmap");
            }
            // 链接实例化 UBO 着色器程序
            if (vsInstUBO != null && fsDef != null)
            {
                var shader = new ShaderProgram(package, vsInstUBO, fsDef);
                shader.SetUniqueName("inst_ubo");
            }
            // 链接实例化 TBO 着色器程序
            if (vsInstTBO != null && fsDef != null)
            {
                var shader = new ShaderProgram(package, vsInstTBO, fsDef);
                shader.SetUniqueName("inst_tbo");
            }
        }
    }
}